/***********************************************************************
 * Module:  LinkPainter.java
 * Author:  Nikola
 * Purpose: Defines the Class LinkPainter
 ***********************************************************************/

package genedit.view.graph;

import genedit.model.graph.BreakPoint;
import genedit.model.graph.GraphElement;
import genedit.model.graph.Link;
import genedit.model.graph.Point;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.ArrayList;

/**
 * Iscrtava veze
 * 
 * @author Igic
 * 
 */
public class LinkPainter extends ElementPainter {
	/** @param element */
	public static final int rectDim = 2;
	public static final int rectEdge = (int) (rectDim * 2 * 1.41);

	public LinkPainter(GraphElement element) {
		super(element);
	}

	public void paint(Graphics2D g) {
		Link link = (Link) element;
		Color backupColor=g.getColor();
		g.setColor(link.getColor());
		Stroke backupStroke = g.getStroke();
		
		if (link.getEnd() == null) {
			g.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0,
					new float[] { 2, 2 }, 0));
		}
		ArrayList<Point> points = link.getAllPoints();
		// ako zelimo da se icrtavaju sve linije ovaj faktor je 0
		int factor = 0;
		if (link.getEnd() != null)
			factor = 1;
		for (int i = 0 + factor; i < points.size() - 1 - factor; i++) {
			g.drawLine((int) points.get(i).getPosition().getX(), (int) points.get(i).getPosition()
					.getY(), (int) points.get(i + 1).getPosition().getX(), (int) points.get(i + 1)
					.getPosition().getY());
		}
		for (Point p : points) {
			// preskoci crtanje pocetnih tacaka
			if (p.equals(link.getStart()) || p.equals(link.getEnd()))
				continue;
			g.fillRect((int) p.getPosition().getX() - rectDim, (int) p.getPosition().getY()
					- rectDim, rectEdge, rectEdge);
		}

		// ako je veza zavrsena crtaj strelice
		link = (Link) getElement();
		if (link.getEnd() != null && link.getBreakPoints().size() > 1) {
			AffineTransform backup = g.getTransform();
			AffineTransform newTrans = new AffineTransform(backup);
			int arrowSize = 10;
			Point breakPoint;
			
			// strelica na pocetku
			if(link.getStartNavigable()){
				breakPoint = points.get(1);
				newTrans.translate(breakPoint.getPosition().getX(), breakPoint.getPosition().getY());
				newTrans.rotate(getAngleOfLineBetweenTwoPoints(breakPoint, points.get(2)));
				g.setTransform(newTrans);
				g.drawLine(0, 0, -arrowSize, arrowSize);
				g.drawLine(0, 0, arrowSize, arrowSize);
			}
			
			// strelica na kraju
			if(link.getEndNavigable()){
				newTrans = new AffineTransform(backup);
				breakPoint = points.get(points.size() - 2);
				newTrans.translate(breakPoint.getPosition().getX(), breakPoint.getPosition().getY());
				newTrans.rotate(getAngleOfLineBetweenTwoPoints(breakPoint,
						points.get(points.size() - 3)));
				g.setTransform(newTrans);
				g.drawLine(0, 0, -arrowSize, arrowSize);
				g.drawLine(0, 0, arrowSize, arrowSize);
			}
			g.setTransform(backup);
		}

		g.setStroke(backupStroke);
		g.setColor(backupColor);
	}

	protected int dist(Point2D linePoint1, Point2D linePoint2, Point2D point) {
		double px = linePoint2.getX() - linePoint1.getX();
		double py = linePoint2.getY() - linePoint1.getY();

		double u = ((point.getX() - linePoint1.getX()) * px + ((point.getY() - linePoint1.getY()) * py))
				/ (px * px + py * py);

		if (u > 1)
			u = 1;
		else if (u < 0)
			u = 0;

		double x = linePoint1.getX() + u * px;
		double y = linePoint1.getY() + u * py;

		double dx = x - point.getX();
		double dy = y - point.getY();

		return (int) Math.sqrt(dx * dx + dy * dy);
	}

	/**
	 * proverava da li se nalazi veza na zadatoj poziciji
	 */
	public boolean isElementAt(Point2D pos) {
		Link link = (Link) element;
		ArrayList<BreakPoint> points = link.getBreakPoints();
		for (int i = 0; i < points.size() - 1; i++) {
			int distance = dist(points.get(i).getPosition(), points.get(i + 1).getPosition(), pos);
			if (distance < 5)
				return true;
		}
		return false;
	}

	/**
	 * definise pravougaonik koji mora biti obuhvacen u laso selekciji da bi
	 * veza bila selektovana
	 * 
	 * @param link
	 * @return
	 */
	public static Rectangle findRectangle(Link link) {
		ArrayList<Point> points = link.getAllPoints();
		double minX = 0, minY = 0, maxX = 0, maxY = 0;
		maxX = minX = points.get(0).getPosition().getX();
		maxY = minY = points.get(0).getPosition().getY();
		for (Point p : points) {
			if (p.getPosition().getX() < minX)
				minX = p.getPosition().getX();
			if (p.getPosition().getX() > maxX)
				maxX = p.getPosition().getX();
			if (p.getPosition().getY() < minY)
				minY = p.getPosition().getY();
			if (p.getPosition().getY() > maxY)
				maxY = p.getPosition().getY();
		}
		Rectangle rect = new Rectangle((int) minX, (int) minY, (int) (maxX - minX),
				(int) (maxY - minY));
		return rect;
	}

	public void refresh() {
		// TODO: implement
	}

	public double getAngleOfLineBetweenTwoPoints(Point p1, Point p2) {
		double xDiff = p2.getPosition().getX() - p1.getPosition().getX();
		double yDiff = p2.getPosition().getY() - p1.getPosition().getY();
		return Math.atan2(yDiff, xDiff) - Math.PI / 2;

	}

}